#$ find . | grep "R$" | xargs grep -n "NormalizeData" --color=auto
# 这几个函数终于出现在 Seurat 包中了！

#(1) 泛型函数
#seurat-4.1.0/R/generics.R:339:NormalizeData <- function(object, ...) {

#' Normalize Data
#'
#' Normalize the count data present in a given assay.
#'
#' @param object An object
#' @param ... Arguments passed to other methods
#'
#' @return Returns object after normalization
#'
#' @rdname NormalizeData
#' @export NormalizeData
#'
NormalizeData <- function(object, ...) {
  UseMethod(generic = 'NormalizeData', object = object)
}




#(2) 按照流程，先是针对 Seurat 对象
#seurat-4.1.0/R/preprocessing.R:2531:NormalizeData.Seurat <- function(


#' @param assay Name of assay to use
#'
#' @rdname NormalizeData
#' @concept preprocessing
#' @export
#' @method NormalizeData Seurat
#'
#' @examples
#' \dontrun{
#' data("pbmc_small")
#' pbmc_small
#' pmbc_small <- NormalizeData(object = pbmc_small)
#' }
#'
NormalizeData.Seurat <- function(
  object,
  assay = NULL,
  normalization.method = "LogNormalize",
  scale.factor = 1e4,
  margin = 1,
  verbose = TRUE,
  ...
) {
  # assay 默认值为默认assay: "RNA"
  assay <- assay %||% DefaultAssay(object = object)

  # 获取该assay对象
  assay.data <- GetAssay(object = object, assay = assay)
  # 对该 assay对象 进行标准化
  assay.data <- NormalizeData(
    object = assay.data,
    normalization.method = normalization.method,
    scale.factor = scale.factor,
    verbose = verbose,
    margin = margin,
    ...
  )

  # 覆盖该Seurat对象的assay
  # 写入的函数定义 seurat-object-4.0.4/R/seurat.R:2616:  f = '[[<-',
  object[[assay]] <- assay.data

  # 记录日志
  object <- LogSeuratCommand(object = object)
  return(object)
}





#(3) 
#seurat-4.1.0/R/preprocessing.R:2494:NormalizeData.Assay <- function(

#' @rdname NormalizeData
#' @concept preprocessing
#' @export
#' @method NormalizeData Assay
#'
NormalizeData.Assay <- function(
  object,
  normalization.method = "LogNormalize",
  scale.factor = 1e4,
  margin = 1,
  verbose = TRUE,
  ...
) {
  object <- SetAssayData( #给 Assay对象@data 赋值
    object = object,
    slot = 'data',

    # 获取新数据：输入的是 RNA@counts 这个df
    new.data = NormalizeData( #这里又调用 NormalizeData.default
      object = GetAssayData(object = object, slot = 'counts'),
      normalization.method = normalization.method,
      scale.factor = scale.factor,
      verbose = verbose, #怎么体现 冗长 输出？
      margin = margin, #干啥的？
      ...
    )
  )
  # 返回 assay 对象
  return(object)
}







(4)
#seurat-4.1.0/R/preprocessing.R:2387:NormalizeData.default <- function(

注释的前2行提示，该函数用到了多核并行化技术。
# Apply Function to Elements in Parallel using Futures 


#' @importFrom future.apply future_lapply
#' @importFrom future nbrOfWorkers
#'
#' @param normalization.method Method for normalization.
#'  \itemize{
#'   \item{LogNormalize: }{Feature counts for each cell are divided by the total
#'   counts for that cell and multiplied by the scale.factor. This is then
#'   natural-log transformed using log1p.}
#'   \item{CLR: }{Applies a centered log ratio transformation}
#'   \item{RC: }{Relative counts. Feature counts for each cell are divided by the total
#'   counts for that cell and multiplied by the scale.factor. No log-transformation is applied.
#'   For counts per million (CPM) set \code{scale.factor = 1e6}}
#' }
#' @param scale.factor Sets the scale factor for cell-level normalization
#' @param margin If performing CLR normalization, normalize across features (1) or cells (2)
# @param across If performing CLR normalization, normalize across either "features" or "cells".
#' @param block.size How many cells should be run in each chunk, will try to split evenly across threads
#' @param verbose display progress bar for normalization procedure
#'
#' @rdname NormalizeData
#' @concept preprocessing
#' @export
#'
NormalizeData.default <- function(
  object,
  normalization.method = "LogNormalize", #好几种标准化方法，这个应用最广泛
  scale.factor = 1e4,
  margin = 1,
  block.size = NULL, #决定是否多核，设定一个进程块的细胞数量
  verbose = TRUE,
  ...
) {
  #(A1) 检查参数 ...
  CheckDots(...)

  #(A2) 如果没有指定方法，直接返回原对象
  if (is.null(x = normalization.method)) {
    return(object)
  }

  #(A3) 如果是多核( 多线程的实现，留给 下一节)
  normalized.data <- if (nbrOfWorkers() > 1) {
  	# 先获取函数名
    norm.function <- switch(
      EXPR = normalization.method,
      'LogNormalize' = LogNormalize,
      'CLR' = CustomNormalize,
      'RC' = RelativeCounts,
      stop("Unknown normalization method: ", normalization.method)
    )

    if (normalization.method != 'CLR') {
      margin <- 2
    }

    tryCatch(
      expr = Parenting(parent.find = 'Seurat', margin = margin),
      error = function(e) {
        invisible(x = NULL)
      }
    )

    dsize <- switch(
      EXPR = margin,
      '1' = nrow(x = object),
      '2' = ncol(x = object),
      stop("'margin' must be 1 or 2")
    )

    chunk.points <- ChunkPoints(
      dsize = dsize,
      csize = block.size %||% ceiling(x = dsize / nbrOfWorkers())
    )

    normalized.data <- future_lapply(
      X = 1:ncol(x = chunk.points),

      FUN = function(i) {
        block <- chunk.points[, i]

        data <- if (margin == 1) {
          object[block[1]:block[2], , drop = FALSE]
        } else {
          object[, block[1]:block[2], drop = FALSE]
        }

        clr_function <- function(x) {
          return(log1p(x = x / (exp(x = sum(log1p(x = x[x > 0]), na.rm = TRUE) / length(x = x)))))
        }

        args <- list(
          data = data,
          scale.factor = scale.factor,
          verbose = FALSE,
          custom_function = clr_function, margin = margin
        )

        args <- args[names(x = formals(fun = norm.function))]

        return(do.call(
          what = norm.function,
          args = args
        ))
      }
    )

    do.call(
      what = switch(
        EXPR = margin,
        '1' = 'rbind',
        '2' = 'cbind',
        stop("'margin' must be 1 or 2")
      ),
      args = normalized.data
    )

  #(A5) 如果是单核
  } else {
    switch(
      
      EXPR = normalization.method, #根据标准化方法，进行相应的运算

      'LogNormalize' = LogNormalize( #默认执行这个
        data = object,
        scale.factor = scale.factor,
        verbose = verbose
      ),
      'CLR' = CustomNormalize(
        data = object,
        custom_function = function(x) {
          return(log1p(x = x / (exp(x = sum(log1p(x = x[x > 0]), na.rm = TRUE) / length(x = x)))))
        },
        margin = margin,
        verbose = verbose
        # across = across
      ),
      'RC' = RelativeCounts(
        data = object,
        scale.factor = scale.factor,
        verbose = verbose
      ),
      stop("Unknown normalization method: ", normalization.method)
    )
  }

  return(normalized.data)
}





() LogNormalize() 调用C++
#/seurat-4.1.0/R/preprocessing.R:622:LogNormalize <- function(data, scale.factor = 1e4, verbose = TRUE) {

#' Normalize raw data
#'
#' Normalize count data per cell and transform to log scale
#'
#' @param data Matrix with the raw count data
#' @param scale.factor Scale the data. Default is 1e4
#' @param verbose Print progress
#'
#' @return Returns a matrix with the normalize and log transformed data
#'
#' @importFrom methods as
#'
#' @export
#' @concept preprocessing
#'
#' @examples
#' mat <- matrix(data = rbinom(n = 25, size = 5, prob = 0.2), nrow = 5)
#' mat
#' mat_norm <- LogNormalize(data = mat)
#' mat_norm
#'
LogNormalize <- function(data, scale.factor = 1e4, verbose = TRUE) {
  # 如果是df，转为矩阵
  if (is.data.frame(x = data)) {
    data <- as.matrix(x = data)
  }
  # 强制转为 稀疏矩阵
  if (!inherits(x = data, what = 'dgCMatrix')) {
    data <- as(object = data, Class = "dgCMatrix")
  }

  # call Rcpp function to normalize
  if (verbose) {
    cat("Performing log-normalization\n", file = stderr())
  }
  
  #这个函数是用 C++ 实现的
  norm.data <- LogNorm(data, scale_factor = scale.factor, display_progress = verbose)

  # 给行名、列名
  colnames(x = norm.data) <- colnames(x = data)
  rownames(x = norm.data) <- rownames(x = data)

  return(norm.data)
}






#$ find . | grep "R$" | xargs grep -n "LogNorm" --color=auto
# seurat-4.1.0/R/RcppExports.R:20:LogNorm <- function(data, scale_factor, display_progress = TRUE) {
LogNorm <- function(data, scale_factor, display_progress = TRUE) {
    .Call('_Seurat_LogNorm', PACKAGE = 'Seurat', data, scale_factor, display_progress)
}


# $ find . | xargs grep -n "_Seurat_LogNorm" --color=auto
#./seurat-4.1.0/src/RcppExports.cpp:412:    {"_Seurat_LogNorm", (DL_FUNC) &_Seurat_LogNorm, 3},
static const R_CallMethodDef CallEntries[] = {
    {"_Seurat_RunModularityClusteringCpp", (DL_FUNC) &_Seurat_RunModularityClusteringCpp, 9},
    {"_Seurat_RunUMISampling", (DL_FUNC) &_Seurat_RunUMISampling, 4},
    {"_Seurat_RunUMISamplingPerCell", (DL_FUNC) &_Seurat_RunUMISamplingPerCell, 4},
    {"_Seurat_RowMergeMatrices", (DL_FUNC) &_Seurat_RowMergeMatrices, 5},
    {"_Seurat_LogNorm", (DL_FUNC) &_Seurat_LogNorm, 3},
...
这个是声明，定义在另一个地方：


#./seurat-4.1.0/src/RcppExports.cpp:77:RcppExport SEXP _Seurat_LogNorm(SEXP dataSEXP, SEXP scale_factorSEXP, SEXP display_progressSEXP) {
// LogNorm
Eigen::SparseMatrix<double> LogNorm(Eigen::SparseMatrix<double> data, int scale_factor, bool display_progress);
RcppExport SEXP _Seurat_LogNorm(SEXP dataSEXP, SEXP scale_factorSEXP, SEXP display_progressSEXP) {
BEGIN_RCPP
    Rcpp::RObject rcpp_result_gen;
    Rcpp::traits::input_parameter< Eigen::SparseMatrix<double> >::type data(dataSEXP);
    Rcpp::traits::input_parameter< int >::type scale_factor(scale_factorSEXP);
    Rcpp::traits::input_parameter< bool >::type display_progress(display_progressSEXP);
    rcpp_result_gen = Rcpp::wrap(LogNorm(data, scale_factor, display_progress));
    return rcpp_result_gen;
END_RCPP
}


似乎干活的是另一个 C++ 函数: LogNorm
继续找: 
$ find . | xargs grep -n "LogNorm" 2>/dev/null | grep -v "LogNormalize" | grep "LogNorm" --color=auto
./seurat-4.1.0/src/data_manipulation.h:24:Eigen::SparseMatrix<double> LogNorm(Eigen::SparseMatrix<double> data, int scale_factor,
./seurat-4.1.0/src/data_manipulation.cpp:115:Eigen::SparseMatrix<double> LogNorm(Eigen::SparseMatrix<double> data, int scale_factor, bool display_progress = true){

// [[Rcpp::export(rng = false)]]
Eigen::SparseMatrix<double> LogNorm(Eigen::SparseMatrix<double> data, int scale_factor, bool display_progress = true){
  Progress p(data.outerSize(), display_progress);
  Eigen::VectorXd colSums = data.transpose() * Eigen::VectorXd::Ones(data.rows());
  for (int k=0; k < data.outerSize(); ++k){
    p.increment();
    for (Eigen::SparseMatrix<double>::InnerIterator it(data, k); it; ++it){
      it.valueRef() = log1p(double(it.value()) / colSums[k] * scale_factor);
    }
  }
  return data;
}

重点关注干活的那一行：log1p(double(it.value()) / colSums[k] * scale_factor);
写成R语句就是 log(x/sum(x)*1e6 +1)  #自然对数

会点C，但是不会C++，没时间，学不动了。大致看着代码猜一下，Rcpp部分放水先。







#$ find . | grep "R$" | xargs grep -n "GetAssay" | grep -v "GetAssayData" | grep "GetAssay" --color=auto
#seurat-4.1.0/R/generics.R:256:GetAssay <- function(object, ...) {

#' Get an Assay object from a given Seurat object.
#'
#' @param object An object
#' @param ... Arguments passed to other methods
#'
#' @return Returns an Assay object
#'
#' @rdname GetAssay
#' @export GetAssay
#'
GetAssay <- function(object, ...) {
  UseMethod(generic = 'GetAssay', object = object)
}



#seurat-4.1.0/R/objects.R:1351:GetAssay.Seurat <- function(object, assay = NULL, ...) {

#' @param assay Assay to get
#'
#' @rdname GetAssay
#' @concept objects
#' @export
#' @method GetAssay Seurat
#'
#' @examples
#' data("pbmc_small")
#' GetAssay(object = pbmc_small, assay = "RNA")
#'
GetAssay.Seurat <- function(object, assay = NULL, ...) {
  CheckDots(...)
  assay <- assay %||% DefaultAssay(object = object)

  # 返回的是字符串 "RNA"
  object.assays <- FilterObjects(object = object, classes.keep = 'Assay') 

  # 如果传入的参数 不在 对象的Assay中，则报错
  if (!assay %in% object.assays) {
    stop(paste0(
      assay,
      " is not an assay present in the given object. Available assays are: ",
      paste(object.assays, collapse = ", ")
    ))
  }
  # 返回 pbmc_small@assays 这个list的键 "RNA" 对应的值: Assay对象
  return(slot(object = object, name = 'assays')[[assay]])
}










() SetAssayData
# seurat-object-4.0.4/R/generics.R:699:SetAssayData <- function(object, slot, new.data, ...) {

#' @param new.data New assay data to add
#'
#' @return \code{SetAssayData}: \code{object} with the assay data set
#'
#' @rdname AssayData
#' @export SetAssayData
#'
#' @order 2
#'
#'
SetAssayData <- function(object, slot, new.data, ...) {
  UseMethod(generic = 'SetAssayData', object = object)
}




# seurat-object-4.0.4/R/assay.R:423:SetAssayData.Assay <- function(
# 前面都是验证，起作用的就最后一句:
slot(object = object, name = slot) <- new.data

#' @importFrom stats na.omit
#'
#' @rdname AssayData
#' @export
#' @method SetAssayData Assay
#'
#' @examples
#' # Set an Assay slot directly
#' count.data <- GetAssayData(pbmc_small[["RNA"]], slot = "counts")
#' count.data <- as.matrix(x = count.data + 1)
#' new.assay <- SetAssayData(pbmc_small[["RNA"]], slot = "counts", new.data = count.data)
#'
SetAssayData.Assay <- function(
  object,
  slot = c('data', 'scale.data', 'counts'),
  new.data,
  ...
) {
  CheckDots(...)
  slot <- slot[1] # 默认取第一个
  slot <- match.arg(arg = slot) # 根据单词前几个字母，自动补齐整个单词


  # 如果 新数据 不是空的
  if (!IsMatrixEmpty(x = new.data)) {
  	# 替换行名(symbol)中的_为-
    if (any(grepl(pattern = '_', x = rownames(x = new.data)))) {
      warning(
        "Feature names cannot have underscores ('_'), replacing with dashes ('-')",
        call. = FALSE,
        immediate. = TRUE
      )
      rownames(x = new.data) <- gsub(
        pattern = '_',
        replacement = '-',
        x = rownames(x = new.data)
      )
    }

    # 如果数据列数和 对象列数(cell id)不等，则报错
    if (ncol(x = new.data) != ncol(x = object)) {
      stop(
        "The new data doesn't have the same number of cells as the current data",
        call. = FALSE
      )
    }
    # 记录对象的行数、行名(symbol)
    num.counts <- nrow(x = object)
    counts.names <- rownames(x = object)
    
    # 如果要覆盖 'scale.data'，且 新数据行数 大于 老对象行数(symbol)，给警告 
    #todo 什么情况下会这样？？
    if (slot == 'scale.data' && nrow(x = new.data) > num.counts) {
      warning(
        "Adding more features than present in current data",
        call. = FALSE,
        immediate. = TRUE
      )
    # 否则，如果 覆盖 counts 或 data，且 数据集行数 不等于 老对象行数，则警告
    } else if (slot %in% c('counts', 'data') && nrow(x = new.data) != num.counts) {
      warning(
        "The new data doesn't have the same number of features as the current data",
        call. = FALSE,
        immediate. = TRUE
      )
    }

    # 如果新基因名不是全在 老对象中，则警告
    if (!all(rownames(x = new.data) %in% counts.names)) {
      warning(
        "Adding features not currently present in the object",
        call. = FALSE,
        immediate. = TRUE
      )
    }

    # 老对象 symbol 在 新数据行名中的 下标，忽略NA
    new.features <- na.omit(object = match(
      x = counts.names,
      table = rownames(x = new.data)
    ))

    # 新数据 细胞名字
    new.cells <- colnames(x = new.data)
    # 如果新细胞名 不全在 老对象中，则报错
    if (!all(new.cells %in% colnames(x = object))) {
      stop(
        "All cell names must match current cell names",
        call. = FALSE
      )
    }

    # 按 行下标，旧列名取子集，保持df结构
    new.data <- new.data[new.features, colnames(x = object), drop = FALSE]

    # 如果是覆盖 counts 或 data，且 新数据 和 旧对象 维度不全一样，则报错
    if (slot %in% c('counts', 'data') && !all(dim(x = new.data) == dim(x = object))) {
      stop(
        "Attempting to add a different number of cells and/or features",
        call. = FALSE
      )
    }
  }

  # 如果新数据 行名 不是向量
  if (!is.vector(x = rownames(x = new.data))) {
  	# 则 行名强制变为向量
    rownames(x = new.data) <- as.vector(x = rownames(x = new.data))
  }

  # 如果 新数据 列名 不是向量
  if (!is.vector(x = colnames(x = new.data))) {
  	# 则 列名强制变为向量
    colnames(x = new.data) <- as.vector(x = colnames(x = new.data))
  }

  # 赋值语句
  slot(object = object, name = slot) <- new.data

  return(object)
}







() IsMatrixEmpty()
$ find . | grep "R$" |xargs grep -n -i "IsMatrixEmpty" --color=auto
#./seurat-object-4.0.4/R/utils.R:264:IsMatrixEmpty <- function(x) {


#' Check if a matrix is empty
#'
#' Takes a matrix and asks if it's empty (either 0x0 or 1x1 with a value of NA)
#'
#' @param x A matrix
#'
#' @return Whether or not \code{x} is empty
#'
#' @export
#'
#' @concept utils
#'
#' @examples
#' IsMatrixEmpty(new("matrix"))
#' IsMatrixEmpty(matrix())
#' IsMatrixEmpty(matrix(1:3))
#'
IsMatrixEmpty <- function(x) {
  # dim 全为0，则为空
  matrix.dims <- dim(x = x) 
  
  #所有dim都为1，且值都是NA，则为空 #todo 这是啥情况?
  matrix.na <- all(matrix.dims == 1) && all(is.na(x = x)) 

  return(all(matrix.dims == 0) || matrix.na)
}








()
$ find . | grep "R$" |xargs grep -n -i "FindObject" --color=auto
#./seurat-object-4.0.4/R/seurat.R:3168:FindObject <- function(object, name) {


测试：
> Seurat:::FindObject(pbmc_small, "pca")
[1] "reductions"


#' Find the collection of an object within a Seurat object
#'
#' @param object A \code{\link{Seurat}} object
#' @param name Name of object to find
#'
#' @return The collection (slot) of the object
#'
#' @keywords internal
#'
#' @noRd
#'
#' @examples
#' \donttest{
#' SeuratObject:::FindObject(pbmc_small, name = "RNA")
#' }
#'
FindObject <- function(object, name) {
  # (A1) 获取list，键是Seurat的slot名，值是这些slot list的names/内部对象的名字
  # Seurat 中的几个slot，都是list，装的都是内部类
  collections <- c(
    'assays',
    'graphs',
    'neighbors',
    'reductions',
    'commands',
    'images'
  )

  object.names <- lapply(
    X = collections, #遍历：Seurat 这几个slot
    FUN = function(x) {
      #返回这几个slot 对应的list的键
      return(names(x = slot(object = object, name = x))) 
    }
  )

  # 输出命名
  names(x = object.names) <- collections
  # 过滤：保留非空的
  object.names <- Filter(f = Negate(f = is.null), x = object.names)

  # (A2) 对非空的 slot 进行循环
  for (i in names(x = object.names)) {
    # 如果输入参数name在这个 slot 的 list 中，则返回该 slot 名字
    if (name %in% names(x = slot(object = object, name = i))) {
      return(i)
    }
  }

  return(NULL)
}

